#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>


using namespace cv;
using namespace std;

vector<Point2d> detection(Mat src)
{
    Mat src_gray;
	int thresh = 52;
	int max_thresh = 255;
	RNG rng(12345);
	/// Convert image to gray and blur it
	cvtColor( src, src_gray, COLOR_BGR2GRAY );
	blur( src_gray, src_gray, Size(3,3) );


	Mat threshold_output;
	vector<vector<Point> > contours;
	vector<vector<Point> > contours_pick;
	vector<Vec4i> hierarchy;

	/// Detect edges using Threshold
	threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
	/// Find contours
	findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );




	/// Get the moments
	vector<Moments> mu(contours.size() );
	vector<Moments> mu_pick;
	for( size_t i = 0; i < contours.size(); i++ )
	 {
	  mu[i] = moments( contours[i], false );
	  if(mu[i].m00>=400 && mu[i].m00<30000)
	  {
		contours_pick.push_back(contours[i]);
		mu_pick.push_back(mu[i]);
	  }
	 }


	/// Approximate contours to polygons + get bounding rects and circles
	vector<vector<Point> > contours_poly( contours_pick.size() );
	vector<Rect> boundRect( contours.size() );
	vector<RotatedRect> ell( contours_pick.size() );
	vector<RotatedRect> ell_pick;
	double height,width;
	vector<Moments> mu_pick2;
	for( size_t i = 0; i < contours_pick.size(); i++ )
	 {
	   approxPolyDP( Mat(contours_pick[i]), contours_poly[i], 3, true );
	   ell[i] = fitEllipse( Mat(contours_poly[i]) );
	   height = ell[i].size.height;
	   width = ell[i].size.width;
	//   cout<<height<<","<<width<<endl;
	   if( height/width > 2)
	   {
		   ell_pick.push_back(ell[i]);
		   mu_pick2.push_back(mu_pick[i]);
	   }
	 }
	///  Get the mass centers:
	vector<Point2d> mc( mu_pick2.size() );
	
	for( size_t i = 0; i < mu_pick2.size(); i++ )
	 { 
	 	mc[i] = Point2f( static_cast<float>(mu_pick2[i].m10/mu_pick2[i].m00) , static_cast<float>(mu_pick2[i].m01/mu_pick2[i].m00) ); 
	 	mc[i].x = cvRound(mc[i].x);
	 	mc[i].y = cvRound(mc[i].y);
	 }
	 // cout<<mc.size()<<endl;
	/// Draw polygonal contour + ellipse
	Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
	for( size_t i = 0; i< contours_pick.size(); i++ )
	   {
		 Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
		 drawContours( drawing, contours_poly, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );
		  circle( drawing, mc[i], 4, color, -1, 8, 0 );
	      ellipse(drawing,ell[i],color,2);
	   }
	// for( size_t i = 0; i< ell_pick.size(); i++ )
	// {
	// 	Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
	// 	ellipse(src,ell_pick[i],color,2);
	// }

	/// Create Window
	const char* source_window = "Source";
	namedWindow( source_window, WINDOW_AUTOSIZE );
	imshow( source_window, src );
	/// Show in a window
	namedWindow( "Contours", WINDOW_AUTOSIZE );
	imshow( "Contours", drawing );
	return mc;
}

Point2d bv(vector<Point2d> mc, Mat src)
{
	Mat matchImg;
	matchImg = imread("44.jpg");
    vector<Rect> rects;  
    unsigned char mask_size=50;
    Point2d target = mc[0];
    unsigned int parallax_min = -1;
    for (size_t i = 0; i < mc.size(); i++)  
    {  
        rects.push_back(Rect(mc[i].x-25, mc[i].y-25, mask_size, mask_size));    
    }  
    // cout<<rects.size()<<endl;
  
	Mat srctemp;
	Mat matchtemp;  
	
	unsigned long sum = 0;
	unsigned long minimum = -1;
	unsigned int mini_pos = 0;

    for(unsigned char i = 0; i < rects.size(); i++)  
     {         
        Rect src_rect(mc[i].x-25, mc[i].y-25, mask_size, mask_size);
        src(src_rect).copyTo(srctemp);  
        // imshow("mask", srctemp);

        // subImages.push_back(srctemp);    
        for(unsigned int j = mc[i].x; j > 25; j--)
        {
        	// cout<<j<<endl;
        	Rect match_rect(j-25, mc[i].y-25, mask_size, mask_size);
        	matchImg(match_rect).copyTo(matchtemp);

        	// imshow("mmask", matchtemp);
        	int nr = matchtemp.rows;
   			int nc = matchtemp.cols * matchtemp.channels();
   			// cout<<nr<<","<<nc<<endl;
   			// ergodic mask
        	for(unsigned char k = 0; k < nr; k++)
    		{
	    		unsigned char *data_src = srctemp.ptr(k);
	    		unsigned char *data_match = matchtemp.ptr(k);
	    
	    		for(unsigned char q = 0; q < nc; q++)
				{
					sum += round(abs(data_match[q]-data_src[q])/100); 		
				}	
		    }
			if(sum < minimum)
			{  
				minimum = sum; 
				mini_pos = j;
			}
			sum = 0;
			
        }
        unsigned int parallax = mc[i].x - mini_pos;
         cout<<" ... "<<"match position: "<<mini_pos<<","<<"parallax: "<<parallax<< endl;
        if(parallax < parallax_min)
        {
        	parallax_min = parallax;
        	target = mc[i];
        }
        minimum = -1;
        mini_pos = 0;
    }  
    return target;
}

int main(void)
{
	Mat src;  
	
	/// Load source image
	src = imread("44.jpg");
	if (src.empty())
	{
		cerr << "No image supplied ..." << endl;
	return -1;
	}
	else
	{
		cout << src.size() << endl;
	}
	vector<Point2d> mc = detection(src);
	if(mc.size())
	{
		cout<<"the candidates are:"<<endl;
		for(unsigned char i=0; i<mc.size(); i++)
		{
 		 cout<<mc[i].x<<","<<mc[i].y<<endl;
		}
		Point2d target = bv(mc, src);
		cout<<"target is :"<<target<<endl;
	}
	

	waitKey(0);
	return 0;
}